/**
 *
 */
package com.cmcc.pcap.jpcap.tcp;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;


import jpcap.packet.TCPPacket;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * @Date 2018年1月17日 上午8:52:40
 * @author Amish
 * (1)存储一个tcp connection对应的所有Packet
 * (2)存储一个tcp connection的辅助信息，如tcp flags信息、最后消息时间等。可用于辅助判断connection状态。
 * (3)提供HTTP事务划分方法
 * (4)提供tcp segment重组的方法
 * (5)提供基于这个TcpConnection的各种统计：包数量、流量、乱序数量、ACK SYN等数量
 */
/**
 * (1)TCP Segment Reassemble (TCP分段重组):
 * (2)TODO TOFIX TCP Out-Of-Order(TCP乱序):应该放到HTTP事务识别之后？
 * (3)TOFIX TCP Previous segment not captured:  do nothing 
 * (4)TCP Dup ACK(ACK重复应答) :Sequence number相同，acknowledgement number也相同的，且“只有”ACK置位，data Length=0，为重复的ACK（注，一个大TCP包的所有分段的对应的ACK的Sequence number相同），TODO 是否需要清洗待定
 * (5)TCP Retransmission(TCP重传):Sequence number相同，acknowledgement number也相同的,data Length!=0
 * (6)TCP Spurious Retransmission(TCP虚假重传,rfc4138):Sequence number相同，acknowlegdement number也相同的,data Length!=0
 */
public class TcpConnection {
	public static Logger logger = LogManager.getLogger(TcpConnection.class);
	
	public String connectionKey;//标识一个Connection的唯一Key，形如  “IP:PORT-IP:PORT”  //在处理Connection的第一个SYN消息时设置
	public String clientAddr;//TCP连接的客户端，形如 “IP:PORT”//在处理Connection的第一个SYN消息时设置
	public String serverAddr;//TCP连接的服务端，形如 “IP:PORT”//在处理Connection的第一个SYN消息时设置
	
	public TcpProtocol protocol = TcpProtocol.TCP;//TCP链接上对应的应用层协议，如HTTP等。默认就是TCP，包括无法识别应用层协议的分片以及无data的包，此处都是TCP。
	public TcpPacketDecorator firstDataTcpPacketDecorator;//第一个带Tcp Payload/data的包 //在向cache中加入该包且filter过滤后设置
	public ArrayList<TcpPacketDecorator> al;//保存该连接初始收到的所有TcpPacket，TODO ArrayList or ConnectionHashMap?  TODO reassemble后是否需要清空？

	public boolean haveRST = false; //有一条RST packet，就置为true
	public boolean haveFin = false; //有一条FIN packet，就置为true
	public long lastPacketTime;//connection最后一个packet的加入cache的时间(注:非抓包时间)
	
	/**
	 * 三次握手对应的包
	 */
	public TCPPacket handshakeTcpPacket_syn;
	public TCPPacket handshakeTcpPacket_syn_ack;
	public TCPPacket handshakeTcpPacket_ack;
	/**
	 * 四次挥手对应的包
	 */
	public TCPPacket waveTcpPacket_clientsend_fin;
	public TCPPacket waveTcpPacket_clientreceive_ack;
	public TCPPacket waveTcpPacket_serversend_fin;
	public TCPPacket waveTcpPacket_serverreceive_ack;
	/**
	 * RST对应的包
	 */
	public TCPPacket tcpPacket_rst;
	
	/**
	 * 此三个list依次依赖前一个list处理，而duplicateAckMergeAl依赖最开始的al
	 */
	public ArrayList<TcpPacketDecorator> duplicateAckMergeAl;//保存Duplicate ACK merge后的所有TcpPacketDecorator
	public ArrayList<TcpPacketDecorator> retransmissionMergeAl;//保存Retransmission/Spurious Retransmission merge后的所有TcpPacketDecorator
	public ArrayList<TcpPacketDecorator> reassembleAl;//保存Segment Reassemble后的所有TcpPacketDecorator
	public ArrayList<TcpPacketDecorator> reorderTcpPacketAl;//保存tcp包乱序重组后的所有TcpPacketDecorator
	
	/**
	 * 部分针对该Connection的统计数据
	 */
	public int packetsNumInitial;//初始包数
	public int packetsNumAfterDuplicateAckMerge;//Duplicate ACK merge后的包数
	public int packetsNumAfterRetransmissionMerge;//Retransmission/Spurious Retransmission merge后包数
	public int packetsNumAfterReorderTcpPacket;//ReorderTcpPacket后的包数
	public int packetsNumAfterReassemble;//Segment Reassemble后的包数
	
	public int OutOfOrderNum = 0;
	
	public TcpConnection(){
		al = new ArrayList<TcpPacketDecorator>();
	}
	

	/**
	 * 需要依次调用：
	 * TODO this.duplicateAckMerge();和this.retransmissionMerge();如果合并，其实可以通过一次遍历完成，可优化性能。
	 * TODO 四个ArrayList如果依次删除其实可以节省内存
	 */
    public void tcpConnectionPacketsDeal(){
    	//TODO TCP Out-Of-Order(TCP乱序)
    	
    	this.duplicateAckMerge();
		this.packetsNumInitial = al.size();
		al.clear();
    	
    	this.retransmissionMerge();
		this.packetsNumAfterDuplicateAckMerge = duplicateAckMergeAl.size();
		duplicateAckMergeAl.clear();
		
		
		this.reOrderTcpPacket();
		this.packetsNumAfterRetransmissionMerge = retransmissionMergeAl.size();
		retransmissionMergeAl.clear();
		
    	this.reassembleTcpPackets();
		this.packetsNumAfterReorderTcpPacket = reorderTcpPacketAl.size();
		reorderTcpPacketAl.clear();
		
//    	this.reassembleTcpPackets();
//		this.packetsNumAfterRetransmissionMerge = retransmissionMergeAl.size();
//		retransmissionMergeAl.clear();
		
		packetsNumAfterReassemble = reassembleAl.size();//记录分段重组后的包数
    }
    
    /**
     * (2)TCP Out-Of-Order(TCP乱序)
     */
    public void reOrderPacket(int init,int size){
    	int p = 0;//p用于标记乱序的数据包
    	int unorder = 0;
		int j=init+1;//j为当前包的下一个同方向包的位置
		while(j<size){
			String i_ip = reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString();
			String j_ip = reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString();
			if(!j_ip.equals(i_ip))
				++j;
			else
				break;
		}
		while(j<size){
			
			//计算IP包头长度
			long ip_header_length = 0;
			if(reorderTcpPacketAl.get(init).tcpPacket.options==null)
				ip_header_length = 20;//没有可选部分，则包头长度是20
			else
				ip_header_length = 20+reorderTcpPacketAl.get(init).tcpPacket.options.size();
			
			//计算TCP包头长度
			long tcp_header_length = 0;
			if(reorderTcpPacketAl.get(init).tcpPacket.option==null)
				tcp_header_length = 20;//没有可选部分，则包头长度是20
			else
				tcp_header_length = 20+reorderTcpPacketAl.get(init).tcpPacket.option.length;
			
			//IP包头和TCP包头长度之和
			long total_header_length = ip_header_length+tcp_header_length;
			
//			long j_sequence = reorderTcpPacketAl.get(j).tcpPacket.sequence;
//			long i_sequence = reorderTcpPacketAl.get(init).tcpPacket.sequence;
//			String i_ip = reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString();
//			String j_ip = reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString();
//			long i_length = reorderTcpPacketAl.get(init).tcpPacket.length-total_header_length;
			
			if((reorderTcpPacketAl.get(j).tcpPacket.fin) && (reorderTcpPacketAl.get(init).tcpPacket.sequence + reorderTcpPacketAl.get(init).tcpPacket.length -total_header_length == reorderTcpPacketAl.get(j).tcpPacket.sequence)) {
				//logger.info("执行了fin" + "sequence:" + " " + reorderTcpPacketAl.get(j).tcpPacket.sequence);
				break;
			}
			else{
				if(reorderTcpPacketAl.get(init).tcpPacket.sequence + reorderTcpPacketAl.get(init).tcpPacket.length -total_header_length == reorderTcpPacketAl.get(j).tcpPacket.sequence){
					init = j;
					j = init+1;
					while(j<size){
						if(!reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString().equals(reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString()))
							++j;
						else
							break;
					}
				}
				else{//当序号不连续时，会有三种情况出现。1，数据包确实有乱序；2，数据包丢包了；3，遇到了序号错乱的数据包，其实在序号错乱的数据包后面有 满足顺序序号的数据包，只是被序号错乱的数据包打断了
					p = j;
					//logger.info("修改了p值" + reorderTcpPacketAl.get(p).tcpPacket.sequence);
					while(j<size){
						if((reorderTcpPacketAl.get(init).tcpPacket.sequence + reorderTcpPacketAl.get(init).tcpPacket.length - total_header_length != reorderTcpPacketAl.get(j).tcpPacket.sequence)&&(reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString().equals(reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString()))) {
							if(reorderTcpPacketAl.get(init).tcpPacket.sequence + reorderTcpPacketAl.get(init).tcpPacket.length - total_header_length < reorderTcpPacketAl.get(j).tcpPacket.sequence)
								++j;
							else {//遇到了乱序数据包，标记乱序，但是不交换
								if((reorderTcpPacketAl.get(j).tcpPacket.ack && reorderTcpPacketAl.get(j).tcpPacket.length - total_header_length == 0)) {
									logger.debug("ack num:" + reorderTcpPacketAl.get(j).tcpPacket.ack + " " + "length num:" + reorderTcpPacketAl.get(j).tcpPacket.length + ";" + reorderTcpPacketAl.get(j).tcpPacket.length + ";" + total_header_length);
									logger.debug("len:" + reorderTcpPacketAl.get(j).tcpPacket.len);
									++j;
								}
								else {
									logger.debug("遇到了乱序数据包:" + reorderTcpPacketAl.get(j).tcpPacket.sequence);
									unorder = 1;
									reorderTcpPacketAl.get(j).isOutOfOrder = true;
									synchronized(this){
										++OutOfOrderNum;
									}
									++j;
								}
							}
						}//如果一直往后找，还是没有找到相应的数据包，则最后j的值会变为一个停车票connection中数据包的总长度，进而跳出循环
						else if((reorderTcpPacketAl.get(init).tcpPacket.sequence + reorderTcpPacketAl.get(init).tcpPacket.length - total_header_length != reorderTcpPacketAl.get(j).tcpPacket.sequence)&&(!reorderTcpPacketAl.get(j).tcpPacket.src_ip.toString().equals(reorderTcpPacketAl.get(init).tcpPacket.src_ip.toString()))){
							++j;
						}//向后寻找同一数据方向的数据包的时候，需要确认数据的方向，否则会出现bug
						else{
							if(unorder == 0) {
								reorderTcpPacketAl.get(j).isOutOfOrder = true;
								synchronized(this){
									++OutOfOrderNum;
								}
								
							}
							break;
						}
					}
					
					//将此刻找到的j位置的数据包插入p位置前面
					if(j<size && unorder == 0) {//此处处理未丢包的情况
						//logger.info("j 的值"+j);
						//logger.info("执行了交换");
//						long p_temp_sequence = reorderTcpPacketAl.get(p).tcpPacket.sequence;
//						long j_temp_sequence = reorderTcpPacketAl.get(j).tcpPacket.sequence;
						
						TcpPacketDecorator temp = reorderTcpPacketAl.get(j);
						reorderTcpPacketAl.remove(j);
						reorderTcpPacketAl.add(p,temp);
						
//						long p_temp_sequence1 = reorderTcpPacketAl.get(p).tcpPacket.sequence;
//						long j_temp_sequence1 = reorderTcpPacketAl.get(j).tcpPacket.sequence;
						
						j = p;
//						long j_temp_sequence2 = reorderTcpPacketAl.get(j).tcpPacket.sequence;
					}
					if(j==size && unorder == 0) {//此处解决出现丢包的问题。有两种情况，一种是丢包后的后续数据包没有乱序；另一种是丢包后的后续数据包存在乱序	
						//logger.info("出现了丢包");
						j = p+1;
						while(j<size) {
							if(reorderTcpPacketAl.get(j).tcpPacket.sequence<reorderTcpPacketAl.get(p).tcpPacket.sequence) {
								reorderTcpPacketAl.get(j).isOutOfOrder = true;
								++OutOfOrderNum;
								TcpPacketDecorator temp = reorderTcpPacketAl.get(j);
								reorderTcpPacketAl.remove(j);
								reorderTcpPacketAl.add(p,temp);
								j = p+1;
								init = p;
							}
							else
								++j;
						}
						if(j == size) {
							init = p;
							j = p+1;
						}
						
					}
					if(unorder ==1) {
						//遇到一个序号错乱的包只对它进行标记，其他什么都不做，所以在下次循环之前必须将标记的值变为初始值
						unorder = 0;
					}
					
				}
			}
			
		}
//		logger.error("=========================================================");
//		logger.error("out of order number"+OutOfOrderNum);
//		logger.error("=========================================================");
    }
    
    
	public void reOrderTcpPacket() {
		reorderTcpPacketAl = new ArrayList<TcpPacketDecorator>();
		Iterator<TcpPacketDecorator> intertor = retransmissionMergeAl.iterator();
		while(intertor.hasNext()){
			reorderTcpPacketAl.add(intertor.next());
		}
		
		//从第四个包开始,向后找到第一个与src_num位置IP不同的IP，记其位置为dst_num,则src_num与dst_num分别是client和server
		int src_num=3;
		int dst_num = src_num+1;
		int size = reorderTcpPacketAl.size();
		
		while(dst_num<size){
			if(reorderTcpPacketAl.get(src_num).tcpPacket.src_ip.toString().equals(reorderTcpPacketAl.get(dst_num).tcpPacket.src_ip.toString()))
				++dst_num;
			else
				break;
		}

		if(src_num != size)
			this.reOrderPacket(src_num,size);
		
		if(dst_num != size)
			this.reOrderPacket(dst_num,size);
		
		logger.debug("out of order packets num : "+OutOfOrderNum);
	}
	
	
//此方法仅用于识别协议为tcp的tcpconnection使用，用于包的乱序重排
	public void reOrderTcpPacket2() {
		reorderTcpPacketAl = new ArrayList<TcpPacketDecorator>();
		Iterator<TcpPacketDecorator> intertor = al.iterator();
		while(intertor.hasNext()){
			reorderTcpPacketAl.add(intertor.next());
		}
		
		//从第四个包开始,向后找到第一个与src_num位置IP不同的IP，记其位置为dst_num,则src_num与dst_num分别是client和server
		int src_num=3;
		int dst_num = src_num+1;
		int size = reorderTcpPacketAl.size();
		
		while(dst_num<size){
			if(reorderTcpPacketAl.get(src_num).tcpPacket.src_ip.toString().equals(reorderTcpPacketAl.get(dst_num).tcpPacket.src_ip.toString()))
				++dst_num;
			else
				break;
		}

		if(src_num != size)
			this.reOrderPacket(src_num,size);
		
		if(dst_num != size)
			this.reOrderPacket(dst_num,size);
		
		logger.debug("out of order packets num : "+OutOfOrderNum);
	}
	
    
	/**
	 * (4) TCP Dup ACK(ACK重复应答)
	 */
	public void duplicateAckMerge(){
		duplicateAckMergeAl = new ArrayList<TcpPacketDecorator>();
		
		
		Iterator<TcpPacketDecorator> intertor = al.iterator();
		while(intertor.hasNext()){
			TcpPacketDecorator tcpPacketDecorator = intertor.next();
			
			if(tcpPacketDecorator.tcpPacket.data.length == 0 ){
				
				int check = this.checkSeqNumAndAckNum(duplicateAckMergeAl, tcpPacketDecorator);
				if(check != -1 ){
					TcpPacketDecorator tpd = duplicateAckMergeAl.get(check);
					tpd.isDuplicateAckMerge = true;
					tpd.duplicateAckMergeAl = new ArrayList<TcpPacketDecorator>();
					tpd.duplicateAckMergeAl.add(tcpPacketDecorator);
				}else {
					duplicateAckMergeAl.add(tcpPacketDecorator);
				}
			}else{
				duplicateAckMergeAl.add(tcpPacketDecorator);
			}
		}
		

//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(duplicateAckMergeAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
		
	}
	
	/**
	 * (5) TCP Retransmission(TCP重传)/(6) TCP Spurious Retransmission(TCP虚假重传,rfc4138)
	 */
	public void retransmissionMerge(){
		retransmissionMergeAl = new ArrayList<TcpPacketDecorator>();
		
		Iterator<TcpPacketDecorator> intertor = duplicateAckMergeAl.iterator();
		while(intertor.hasNext()){
			TcpPacketDecorator tcpPacketDecorator = intertor.next();
			
            if(tcpPacketDecorator.tcpPacket.data.length != 0 ){
				
				int check = this.checkSeqNumAndAckNum(retransmissionMergeAl, tcpPacketDecorator);
				if(check != -1 ){
					TcpPacketDecorator tpd = retransmissionMergeAl.get(check);
					tpd.isRetransmissionMerge = true;
					tpd.retransmissionMergeAl = new ArrayList<TcpPacketDecorator>();
					tpd.retransmissionMergeAl.add(tcpPacketDecorator);
				}else {
					retransmissionMergeAl.add(tcpPacketDecorator);
				}
			}else{
				retransmissionMergeAl.add(tcpPacketDecorator);
			}
		}
		

//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(retransmissionMergeAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	}

	/**
	 * (1) TCP Segment Reassemble (TCP分段重组)
	 * PS:所有的Acknowledgement Num和Sequence number应该通过一个ISN(Inital Sequence Number),得到一个relative sequence number 和 relative ack number，方便查看和计算，模仿wireshark
	 * 
	 * 分段重组逻辑：
	 * 1、data length!=0才有可能是分段
	 * 2、PSH==1,可能是   a非分段的消息；c分段的中间某个；d分段的最后一个；
	 * 3、PSH!=1,可能是   b分段的第一个；c分段的中间某个；
	 * 4、一个包的不同分段Sequence number是由小到大连续的(当然会有乱序的情况)；
	 * 5、一个包的不同分段Acknowledgment是相同的(都是对之前对向包的数据长度+Sequence number)；
	 */
	public void reassembleTcpPackets(){
		
		reassembleAl = new ArrayList<TcpPacketDecorator>();//重组后的包List
		
		Iterator<TcpPacketDecorator> intertor = reorderTcpPacketAl.iterator();
//		Iterator<TcpPacketDecorator> intertor = retransmissionMergeAl.iterator();
		while(intertor.hasNext()){
			TcpPacketDecorator tcpPacketDecorator = intertor.next();
			
			//this.testOnly(tcpPacketDecorator);//Test Only
				
			if(tcpPacketDecorator.tcpPacket.data.length != 0 ){
				
				int check = this.checkAckNum(reassembleAl, tcpPacketDecorator);
				if(check != -1){
					TcpPacketDecorator tpd = reassembleAl.get(check);
					if(tpd.isReassemble == true) {
						tpd.segmentsAl.add(tcpPacketDecorator);
					}else {
						tpd.isReassemble = true;
						tpd.segmentsAl = new ArrayList<TcpPacketDecorator>();
						tpd.segmentsAl.add(tcpPacketDecorator);
					}
					
				}else{
					reassembleAl.add(tcpPacketDecorator);
				}
			}else{
				reassembleAl.add(tcpPacketDecorator);
			}
		}

//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//		logger.error(reassembleAl.size());
//		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
		
		
		
	}
	/**
	 * 用于判断TCP 分段的 Ack Num,同时需要检查是否有data、数据方向是否相同
	 * @param
	 * @param tpd
	 * @return
	 */
	private int checkAckNum(ArrayList<TcpPacketDecorator> argAl,TcpPacketDecorator tpd){
		for(TcpPacketDecorator saveTpd :argAl){
			if(saveTpd.tcpPacket.data.length >0
					&& saveTpd.tcpPacket.ack_num == tpd.tcpPacket.ack_num 
					&& saveTpd.tcpPacket.src_ip.getHostAddress().equalsIgnoreCase(tpd.tcpPacket.src_ip.getHostAddress()) 
					&& saveTpd.tcpPacket.src_port == tpd.tcpPacket.src_port ){
				return argAl.indexOf(saveTpd);
			}
		}
		return -1;
	}
	/**
	 * 用于判断Duplicate ACK/以及 TCP Spurious Retransmission/TCP Retransmission的情况，同时需要检查数据方向
	 * PS 注意，最后一个无数据的ACK和(RST+ACK)或(FIN+ACK)的Ack Num和Seq Num可能相同
	 * @param
	 * @param tpd
	 * @return
	 */
	private int checkSeqNumAndAckNum(ArrayList<TcpPacketDecorator> argAl,TcpPacketDecorator tpd){
		for(TcpPacketDecorator saveTpd :argAl){
			if(this.checkFlags(saveTpd, tpd) 
					&& saveTpd.tcpPacket.ack_num == tpd.tcpPacket.ack_num 
					&& saveTpd.tcpPacket.sequence == tpd.tcpPacket.sequence
					&& saveTpd.tcpPacket.src_ip.getHostAddress().equalsIgnoreCase(tpd.tcpPacket.src_ip.getHostAddress()) 
					&& saveTpd.tcpPacket.src_port == tpd.tcpPacket.src_port 
					&& saveTpd.tcpPacket.data.length== tpd.tcpPacket.data.length){
				return argAl.indexOf(saveTpd);
			}
		}
		return -1;
	}
	
	/**
	 * 检查两个包的tcp flags是否完全相同
	 * @param saveTpd
	 * @param tpd
	 * @return
	 */
	private boolean checkFlags(TcpPacketDecorator saveTpd,TcpPacketDecorator tpd){		
		return (saveTpd.tcpPacket.syn == tpd.tcpPacket.syn) 
				&& ((saveTpd.tcpPacket.ack == tpd.tcpPacket.ack)) 
				&& ((saveTpd.tcpPacket.rst == tpd.tcpPacket.rst)) 
				&& ((saveTpd.tcpPacket.fin == tpd.tcpPacket.fin));
	}
	
	
	
	
	public int downPakcetNum(){//TODO TODO TODO TODO TODO
		return 0;
	}
	public int upPacketNum(){//TODO TODO TODO TODO TODO
		return 0;
	}
	public int downFlow(){//TODO TODO TODO TODO TODO
		return 0;
	}
	public int upFlow(){//TODO TODO TODO TODO TODO
		return 0;
	}
	
	public void testOnly(TcpPacketDecorator tpd){
		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
		TCPPacket tp = tpd.tcpPacket;
		logger.error(""+tp.ack_num);
		logger.error(""+tp.sequence);
		logger.error("packet.len:"+tp.len);
		logger.error("packet.caplen:"+tp.caplen);
		logger.error("ippacket.length:"+tp.length);
		logger.error("protocol:"+tp.protocol);
		logger.error("offset:"+tp.offset);
		logger.error("urgent_pointer:"+tp.urgent_pointer);
		logger.error("version:"+tp.version);
		logger.error("window:"+tp.window);
		logger.error("ack:"+tp.ack);
		logger.error("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	}

}
